#include "StdThreadPool.h"
#include "DoubleLinkList.h"
#include "LinkQueue.h"
#include "StdThread.h"

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <stdbool.h>
#include <unistd.h>

// 线程池

//结构体 : 维护和保存线程指针

// 还需要一个任务的数据结构：任务队列---先进来的先处理

struct StdThreadPool
{
    DLlist threads;   // 线程链表
    LQueue *taskQueue;// 任务队列
    int MaxThrdNum;   // 最大线程数
    int MinThrdNum;   // 最小线程数
    int MaxQueueSize; // 最大任务数量

    int BusyThrdNum; // 忙碌的线程数量
    int MinusNum;    // 需要减少的线程
    // 加锁是因为防止BusyThrdNum被竞争被共享出现数值上的偏差
    Mutex* BusyThrdMutex;// 忙线程锁🔒

    Mutex* PoolMutex;// 线程池 锁🔒

    Cond* QueueNotFull;// 队列不满的条件变量
    Cond* QueueNotEmpty;// 队列不为空的条件变量

    Thread *manager; // 管理者线程
    int SleepTime;// 管理者线程的管理间隔

    // 得有一个标志位让线程停下来
    bool IsClose;// 是否关闭线程池

};

// 任务结构体
typedef struct
{
    void* (*func)(void *);
    void *arg;
}Task;

// 创建任务
Task* CreateTask(void* (*func)(void *), void *arg)
{
    Task* t = (Task *)malloc(sizeof(Task));
    if (t == NULL)
    {
        printf("createTask malloc error!\n");
        return NULL;
    }

    t->func = func;
    t->arg = arg;
    return t;
}

// 判断线程号是否相等
bool IsThreadEqual(void *thread1, void *thread2)
{
    Thread *t = (Thread *)thread1;
    return IsCurrentThread(t);
}

// 线程工作任务
void* ThreadWork(void* arg)
{
    ThreadP *p = (ThreadP *)arg;
    while(p->IsClose == false)
    {
        //// 取任务逻辑
        // 先加锁🔒
        MutexLock(p->PoolMutex);
        
        // 任务队列为空得等
        while(IsQueueEmpty(p->taskQueue) == true)
        {
            CondWait(p->QueueNotEmpty, p->PoolMutex);

            if (p->IsClose == true)
            {
                MutexUnlock(p->PoolMutex);
                ThreadExit();
            }

            // 如果需要削减线程
            if (p->MinusNum > 0)
            { 
                RemoveByElement(&p->threads, NULL, IsThreadEqual, free);
                p->MinusNum--;
                MutexUnlock(p->PoolMutex);
                ThreadExit(); // 线程退出   
            }
        }
        
        Task * t = (Task *)*GetQFront(p->taskQueue);// 先 取值 变成一级指针 再取
        QPop(p->taskQueue);
        CondSignal(p->QueueNotFull);// 唤醒
        MutexUnlock(p->PoolMutex);

        // 加锁是因为防止BusyThrdNum被竞争被共享出现数值上的偏差
        MutexLock(p->BusyThrdMutex);
        p->BusyThrdNum++;
        MutexUnlock(p->BusyThrdMutex);

        // 做任务
        t->func(t->arg);

        MutexLock(p->BusyThrdMutex);
        p->BusyThrdNum--;
        MutexUnlock(p->BusyThrdMutex);

        // 销毁任务
        free(t);
    }
    return NULL; 
}

// 管理者线程
void* ThreadManager(void *arg)
{
    ThreadP *p = (ThreadP *)arg;
    while(p->IsClose == false)
    {
        sleep(p->SleepTime);
        MutexLock(p->PoolMutex);

        // 线程多了
        if ( p->BusyThrdNum * 2 <= p->threads.len)
        {
            p->MinusNum = (p->threads.len - p->BusyThrdNum) / 2;
            if (p->threads.len - p->MinThrdNum < p->MinusNum)
                p->MinusNum = p->threads.len - p->MinThrdNum;
            CondBroadcast(p->QueueNotEmpty);
        }
        // 线程少了
        else if (GetQueueLen(p->taskQueue) >= p->BusyThrdNum)
        {
            int addNum = GetQueueLen(p->taskQueue) / 2;
            if (addNum + p->threads.len >= p->MaxThrdNum)
                addNum = p->MaxThrdNum - p->threads.len;
            for (int i = 0; i < addNum; i++)
            {
                Thread *t = ThreadInit(ThreadWork, p);
                // 把线程塞到线程列表里面
                InsertDLlistTial(&p->threads, t);         // 插入新的线程
            }
        }
        MutexUnlock(p->PoolMutex);
    }
    return NULL;
}

// 初始化
ThreadP *ThreadPoolInit(int MaxThrdNum, int MinThrdNum, int MaxQueueSize, int SleepTime)
{
    ThreadP *pool = (ThreadP *)malloc(sizeof(ThreadP));
    if (pool == NULL)
    {
        printf("ThreadPoolInit malloc error\n");
        return NULL;
    }

    // 一个个初始化
    InitDLlist(&pool->threads);        // 线程链表初始化
    pool->taskQueue = InitLQueue();    // 任务队列初始化

    pool->PoolMutex = MutexInit();     // 初始化线程池锁🔒
    pool->BusyThrdMutex = MutexInit(); // 初始化忙线程锁🔒

    pool->QueueNotFull = CondInit();   // 初始化队列不满的条件变量
    pool->QueueNotEmpty = CondInit();  // 初始化队列为空的条件变量

    pool->IsClose = false;             // 初始化的时候不关

    pool->MaxQueueSize = MaxQueueSize;
    pool->MaxThrdNum = MaxThrdNum;
    pool->MinThrdNum = MinThrdNum;
    pool->BusyThrdNum = 0;
    pool->MinusNum = 0;

    // 创建线程：准备一个池子
    for (int i = 0; i < pool->MaxThrdNum; i++)
    {
        Thread *t = ThreadInit(ThreadWork, pool);
        // 把线程塞到线程列表里面
        InsertDLlistTial(&pool->threads, t);         // 插入新的线程
    }

    pool->manager = ThreadInit(ThreadManager, pool); // 初始化管理者线程
    pool->SleepTime = SleepTime;

    return pool;
}

// 线程池添加任务
void ThreadPoolAddTask(ThreadP *p, void *(*func)(void *), void *arg)
{
    // 先上锁
    MutexLock(p->PoolMutex);
    // 处理任务队列
    // 任务达到上限
    while(GetQueueLen(p->taskQueue) == p->MaxQueueSize)
    {
        // 等待一个条件变量将任务取走，就不满了
        CondWait(p->QueueNotFull, p->PoolMutex);// 等待队列不满的条件
        if (p->IsClose == true)
        {
            MutexUnlock(p->PoolMutex);
            return;// 手动关闭
        }
    }

    QPush(p->taskQueue, CreateTask(func, arg));// 添加任务
    CondSignal(p->QueueNotEmpty);
    MutexUnlock(p->PoolMutex);
}

// 手动关闭线程池
void ClearThreadPool(ThreadP *p)
{
    p->IsClose = true;

    // 管理者线程先退
    ThreadJoin(p->manager);
    ClearThread(p->manager);

    // 任务队列回收
    CondBroadcast(p->QueueNotFull);
    ClearLQueue(p->taskQueue, free);

    // 线程列表回收
    CondBroadcast(p->QueueNotEmpty);
    for (int i = 0; i < p->threads.len; i++)
    {
        ThreadJoin(*FindByIndex(&p->threads, i));// 等任务执行完在回收
    }
    ClearDLlist(&p->threads, free);

    // 锁和条件变量
    ClearMutex(p->BusyThrdMutex);
    ClearMutex(p->PoolMutex);

    ClearCond(p->QueueNotEmpty);
    ClearCond(p->QueueNotFull);

    free(p);
}
